package com.eqdd.annotation.compiler.datasave;

import com.eqdd.annotation.datasave.DealCodeClass;
import com.eqdd.yiqidian.rxbus2.Subscribe;
import com.eqdd.yiqidian.rxbus2.ThreadMode;
import com.eqdd.yiqidian.utils.GreenDaoUtil;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;

/**
 * @author hiphonezhu@gmail.com
 * @version [CompilerAnnotation, 17/6/20 09:55]
 */
@AutoService(Processor.class)
@SupportedAnnotationTypes({"com.eqdd.annotation.datasave.DealCodeClass", "com.eqdd.yiqidian.rxbus2.Subscribe"})
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class DealCodeInjectProcessor extends AbstractProcessor {
    // 存放同一个Class下的所有注解
    Map<String, List<MothedInfo>> classMap = new HashMap<>();
    Map<String, List<Integer>> classCodesMap = new HashMap<>();
    // 存放Class对应的TypeElement
    Map<String, TypeElement> classTypeElement = new HashMap<>();

    private Filer filer;
    Elements elementUtils;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        filer = processingEnv.getFiler();
        elementUtils = processingEnv.getElementUtils();
        ClassName hoverboard = ClassName.get("com.mattel", "Hoverboard");
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        collectInfo(roundEnvironment);
        writeToFile();
        return true;
    }

    void collectInfo(RoundEnvironment roundEnvironment) {
        classMap.clear();
        classTypeElement.clear();

        Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(DealCodeClass.class);
        Set<? extends Element> elementsSub = roundEnvironment.getElementsAnnotatedWith(Subscribe.class);
        for (Element element : elementsSub) {
            List<? extends AnnotationMirror> annotationMirrors = element.getAnnotationMirrors();
            AnnotationMirror annotationMirror = annotationMirrors.get(0);
            Map<? extends ExecutableElement, ? extends AnnotationValue> elementValues = annotationMirror.getElementValues();
            Iterator<? extends Map.Entry<? extends ExecutableElement, ? extends AnnotationValue>> iterator = elementValues.entrySet().iterator();
            Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> next = iterator.next();
            ExecutableElement key = next.getKey();
            String s = key.toString();
            if (s.equals("codes()")) {
                AnnotationValue value = next.getValue();
                String s1 = value.toString();
                // 备注解元素所在的Class
                TypeElement typeElement = (TypeElement) element.getEnclosingElement();
                // Class的完整路径
                String classFullName = typeElement.getQualifiedName().toString();
                List<Integer> codeList = classCodesMap.get(classFullName);
                if (codeList == null) {
                    codeList = new ArrayList<Integer>();
                    classCodesMap.put(classFullName, codeList);
                }
                String replace = s1.replace(" ", "").replace("{", "").replace("}", "");
                String[] splits = replace.split(",");
                for (String split : splits) {
                    codeList.add(Integer.parseInt(split));
                }
            }
//            String s = value.toString();
//
//            String replace = s.replace("{", "").replace("}", "");
//            String[] splits = replace.split(",");
        }
        for (Element element : elements) {
            // 获取 BindView 注解的值
            // 代表被注解的元素
            ExecutableElement variableElement = (ExecutableElement) element;

            // 备注解元素所在的Class
            TypeElement typeElement = (TypeElement) variableElement.getEnclosingElement();
            // Class的完整路径
            String classFullName = typeElement.getQualifiedName().toString();

            // 收集Class中所有被注解的元素
            List<MothedInfo> variableList = classMap.get(classFullName);
            if (variableList == null) {
                variableList = new ArrayList<>();
                classMap.put(classFullName, variableList);

                // 保存Class对应要素（名称、完整路径等）
                classTypeElement.put(classFullName, typeElement);
            }
            MothedInfo variableInfo = new MothedInfo();
            variableInfo.setExecutableElement(variableElement);
            variableList.add(variableInfo);
        }
    }

    void writeToFile() {
        try {
            for (String classFullName : classMap.keySet()) {
                TypeElement typeElement = classTypeElement.get(classFullName);
                List<MothedInfo> variableList = classMap.get(classFullName);
                int num = 0;
                ArrayList<MethodSpec.Builder> builders = new ArrayList<>();

                MethodSpec.Builder construct = MethodSpec.constructorBuilder()
                        .addModifiers(Modifier.PUBLIC)
                        .addParameter(Object.class, "activity")
                        .addStatement("this.$N = $N", "activity", "activity");
                builders.add(construct);
                for (MothedInfo variableInfo : variableList) {
                    num++;
                    ArrayList<Integer> codes = new ArrayList<>();
                    getCodes(variableInfo, codes);
                    StringBuilder stringBuilder = null;
                    StringBuilder stringNumBuilder = null;
                    for (Integer code : codes) {
                        if (stringBuilder == null) {
                            stringBuilder = new StringBuilder("{");
                        }
                        stringBuilder.append(code).append(",");
                        if (stringNumBuilder == null) {
                            stringNumBuilder = new StringBuilder(".getNum(").append(code).append(")+");
                        } else {
                            stringNumBuilder.append("GreenDaoUtil.getNum(").append(code).append(")+");
                        }
                    }
                    if (stringBuilder == null) {
                        continue;
                    }
                    StringBuilder append = stringBuilder.deleteCharAt(stringBuilder.lastIndexOf(",")).append("}");
                    StringBuilder appendSum = stringNumBuilder.deleteCharAt(stringNumBuilder.lastIndexOf("+"));
                    AnnotationSpec.Builder builder = AnnotationSpec.builder(Subscribe.class);
                    builder.addMember("codes", append.toString());
                    builder.addMember("threadMode", "$T.MAIN", ThreadMode.class);
                    MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("update" + num)
                            .addModifiers(Modifier.PUBLIC)
                            .addAnnotation(builder.build());
                    methodBuilder.addStatement("try {\n" +
                                    "            Class<?> proxy = Class.forName($S);\n" +
                                    "            $T method = proxy.getDeclaredMethod($S, int.class);\n" +
                                    "            method.invoke(activity, $T" + appendSum + ");\n" +
                                    "        } catch (Exception e) {\n" +
                                    "            e.printStackTrace();\n" +
                                    "        }", classFullName, Method.class, variableInfo.getExecutableElement().getSimpleName().toString(),
                            GreenDaoUtil.class);
                    builders.add(methodBuilder);
                }
                // 构建Class
                TypeSpec.Builder builder = TypeSpec.classBuilder(typeElement.getSimpleName() + "$$DealCodeInjector")
                        .addField(Object.class, "activity", Modifier.PRIVATE)
                        .addModifiers(Modifier.PUBLIC);
                for (int i = 0; i < builders.size(); i++) {
                    builder.addMethod(builders.get(i).build());
                }
                TypeSpec typeSpec = builder.build();

                // 与目标Class放在同一个包下，解决Class属性的可访问性
                String packageFullName = elementUtils.getPackageOf(typeElement).getQualifiedName().toString();
                JavaFile javaFile = JavaFile.builder(packageFullName, typeSpec)
                        .build();
                // 生成class文件
                javaFile.writeTo(filer);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    private void getCodes(MothedInfo variableInfo, List<Integer> codes) {
//        DealCodeClass annotation = variableInfo.getExecutableElement().getAnnotation(DealCodeClass.class);
        List<? extends AnnotationMirror> annotationMirrors = variableInfo.getExecutableElement().getAnnotationMirrors();
        AnnotationMirror annotationMirror = annotationMirrors.get(0);
        Map<? extends ExecutableElement, ? extends AnnotationValue> elementValues = annotationMirror.getElementValues();
        Iterator<? extends Map.Entry<? extends ExecutableElement, ? extends AnnotationValue>> iterator = elementValues.entrySet().iterator();
        Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> next = iterator.next();
        AnnotationValue value = next.getValue();

        String s = value.toString();

        String replace = s.replace("{", "").replace("}", "").replace(" ", "");
        String[] splits = replace.split(",");
        for (String split : splits) {
            split = split.replace(".class", "");
            if (classMap.get(split) != null) {
                List<MothedInfo> mothedInfos = classMap.get(split);
                for (MothedInfo mothedInfo : mothedInfos) {
                    getCodes(mothedInfo, codes);
                }
            }
            getSingCode(codes, split);
        }

    }

    private void getSingCode(List<Integer> codes, String split) {
        List<Integer> integers = classCodesMap.get(split);
        if (integers != null) {
            codes.addAll(integers);
        }
//        Class<?> aClass = null;
//        try {
//            aClass = Class.forName(split);
//        } catch (ClassNotFoundException e) {
//            e.printStackTrace();
//        }
//        Annotation[] annotationsByType = aClass.getAnnotationsByType(Subscribe.class);
//        for (Annotation annotation1 : annotationsByType) {
//
//            int code = ((Subscribe) annotation1).code();
//            if (code == -1) {
//                int[] subCodes = ((Subscribe) annotation1).codes();
//                for (int subCode : subCodes) {
//                    codes.add(subCode);
//                }
//            } else {
//                codes.add(code);
//            }
//        }
    }
}
